package com.hxkj.token;

import com.alibaba.fastjson.JSONObject;
import com.hxkj.domain.po.LoginToken;
import com.hxkj.domain.po.Token;
import com.hxkj.token.enums.UserRedisKeyEnum;
import com.hxkj.utils.AESUtil;
import com.platform.common.domain.exception.CustomException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


/**
 * token生成工具
 */
@Configuration
@Slf4j
@Data
public class TokenGenerator {
    private static final String ACCESS_TOKEN = "accessToken";

    private static final String REFRESH_TOKEN = "refreshToken";

    // accessToken有效期 1 小时
    private Integer accessTokenExpire = 1;
    // refreshToken有效期 20 小时
    private Integer refreshTokenExpire = 20;
    // redis存储方式 参数分别为:userId type createDate
    private String tokenRedisKey = "token:{0}:{1}:{2}";

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 生成token
     *
     * @param mobile
     * @param account
     * @param userId
     * @return
     * @throws Exception
     */
    public LoginToken generatorLoginToken(String mobile, String account, String userId) throws Exception {
        long createTime = System.currentTimeMillis();
        String accessToken = generatorToken(mobile, account, userId, ACCESS_TOKEN, accessTokenExpire, createTime);
        String refreshToken = generatorToken(mobile, account, userId, REFRESH_TOKEN, refreshTokenExpire, createTime);

        LoginToken loginToken = new LoginToken();
        // accessToken
        HashMap<String, Object> accessTokenMap = new HashMap<>();
        accessTokenMap.put("accessToken", accessToken);
        accessTokenMap.put("expire", getDate(accessTokenExpire));
        loginToken.setAccessToken(accessTokenMap);
        // refreshToken
        HashMap<String, Object> refreshTokenMap = new HashMap<>();
        refreshTokenMap.put("refreshToken", refreshToken);
        refreshTokenMap.put("expire", getDate(refreshTokenExpire));
        loginToken.setRefreshToken(refreshTokenMap);
        return loginToken;
    }

    /**
     * 生成token
     *
     * @param userId
     * @return
     * @throws Exception
     */
    private String generatorToken(String mobile, String account, String userId, String type, Integer expires, long createTime) throws Exception {
        Token token = new Token();
        if (!StringUtils.isEmpty(mobile)) {
            token.setMobile(mobile);
        }
        token.setUserId(userId);
        token.setAccount(account);
        token.setType(type);
        token.setCreateDate(createTime);
        token.setExpires(expires);
        // AES加密json token
        String encryptToken = AESUtil.encryptToken(JSONObject.toJSONString(token));
        // 生成token 加入生成时间
        redisTemplate.opsForValue().set(MessageFormat.format(tokenRedisKey, token.getUserId(), token.getType(), token.getCreateDate()), "1", token.getExpires(), TimeUnit.HOURS);
        return encryptToken;
    }

    /**
     * 验证token是否有效
     *
     * @param tokenString
     * @return
     */
    public Token validateToken(String tokenString) {
        try {
            if (StringUtils.isBlank(tokenString)) {
                return null;
            }
            // AES解密token
            String decryptToken = AESUtil.decryptToken(tokenString);
            Token token = JSONObject.parseObject(decryptToken, Token.class);
            if (token == null) {
                return null;
            }
            // 验证token是否有效
            boolean existOk = redisTemplate.hasKey(MessageFormat.format(tokenRedisKey, token.getUserId(), token.getType(), token.getCreateDate()));
            if (!existOk) {
                return null;
            }

            return token;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 刷新token
     *
     * @throws Exception
     */
    public LoginToken refreshToken(String tokenString) throws Exception {
        Token token = validateToken(tokenString);
        LoginToken loginToken = generatorLoginToken(token.getMobile(), token.getAccount(), token.getUserId());
        redisTemplate.delete(MessageFormat.format(tokenRedisKey, token.getUserId(), ACCESS_TOKEN, token.getCreateDate()));
        redisTemplate.delete(MessageFormat.format(tokenRedisKey, token.getUserId(), REFRESH_TOKEN, token.getCreateDate()));
        return loginToken;
    }


    /**
     * 退出登录
     *
     * @throws Exception
     */
    public void quit() throws Exception {
        ServletRequestAttributes servletRequestAttributes = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes());
        HttpServletRequest request = servletRequestAttributes.getRequest();
        String accesstoken = request.getHeader(ACCESS_TOKEN);
        if (StringUtils.isEmpty(accesstoken)) {
            // 若header中没有,则从参数中获取
            accesstoken = request.getParameter(ACCESS_TOKEN);
        }
        Token token = this.validateToken(accesstoken);
        redisTemplate.delete(MessageFormat.format(tokenRedisKey, token.getUserId(), ACCESS_TOKEN, token.getCreateDate()));
    }

    public void quit(String tokenString) throws Exception {
        Token token = this.validateToken(tokenString);
        if (Objects.nonNull(token))
            redisTemplate.delete(MessageFormat.format(tokenRedisKey, token.getUserId(), ACCESS_TOKEN, token.getCreateDate()));
    }


    /**
     * 清除token用户信息
     *
     * @param userId
     * @throws Exception
     */
    public void invalidUserInfo(String userId) throws Exception {
        if (redisTemplate.hasKey(MessageFormat.format(UserRedisKeyEnum.USER_INFO.getKey(), userId))) {
            redisTemplate.delete(MessageFormat.format(UserRedisKeyEnum.USER_INFO.getKey(), userId));
        }
    }

    /**
     * 获取token到期日
     *
     * @return
     */
    private String getDate(int amount) {
        Calendar now = Calendar.getInstance();
        now.add(Calendar.HOUR, amount);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = sdf.format(now.getTimeInMillis());
        return dateStr;
    }

    public String getUserIdByToken(String accessToken) {
        Token token = JSONObject.parseObject(AESUtil.decryptToken(accessToken), Token.class);
        return token.getUserId();
    }
}
